//============================================================================
// Nombre		: IUC.cpp
// Materia		: Organizacion de Datos (75.06) - FIUBA
// Catedra		: AZCURRA-GUTIERREZ-SANTOS
// Autor		: Tkaczyszyn, Muszalski, Gallinal, Fernandez
// Descripcion	: TPDatos , Ansi-style
//============================================================================

#ifndef _CLIST_H
#define	_CLIST_H

#include <stdio.h>

template <class T> class tLista;

template <class T>
class tNodoLista{
	friend class tLista<T>;
private:
	tNodoLista(T *xdato, tNodoLista<T> *xnodo = NULL){
		dato = xdato;
		siguiente = xnodo;
	}
	T *dato;
	tNodoLista<T> *siguiente;
};

template <class T>
class tLista{
private:
	tNodoLista<T> *lista;
	tNodoLista<T> *ultimoNodo;
	tNodoLista<T> *nodoActual;

public:
	tLista(){ lista = ultimoNodo = nodoActual = NULL; };
	tLista(tLista<T> &list);
	~tLista(){ vaciarLista(); };

	void agregar(T *xdato);
	void agregarOrdenado(T *xdato);
	void agregarAlFinal(T *xdato);
	bool eliminar(T *xdato);
	void vaciarLista();
	bool buscar(T *xdato);

	bool estaVacia(){ return lista == NULL ? true : false; };

	tLista<T> operator=(tLista<T> list);

	void primero();
	bool finLista();
	T *obtenerActual();
	T *obtenerUltimo();
	T *obtenerNodo();

	inline bool operator>(T& d2 )
	{
		return (this > d2 );
	};

};

template <class T>
/* Contructor de copia */
tLista<T>::tLista(tLista<T> &list){
	lista = ultimoNodo = nodoActual = NULL;

	list.primero();
	while (!list.finLista())
		agregarAlFinal(list.obtenerActual());

}

template <class T>
/* Metodo: agregar
 * Agrega un elemento al principio de la lista.*/
void tLista<T>::agregar(T *xdato){
	tNodoLista<T> *nuevoNodo = new tNodoLista<T>(xdato, lista);

	if (lista == NULL)
		ultimoNodo = nuevoNodo;

	lista = nuevoNodo;
}

template <class T>
/* Metodo: agregarOrdenado
 * Agrega un elemento en forma ordenada. NOTA: debe estar
 * implementado el operador > de la clase. */
void tLista<T>::agregarOrdenado(T *xdato){
	bool stop = false;
	tNodoLista<T> *nuevoNodo = new tNodoLista<T>(xdato, NULL);
	tNodoLista<T> *nodoAnterior = NULL;
	tNodoLista<T> *nodoActual = NULL;

	if (lista == NULL){
		ultimoNodo = nuevoNodo;
		lista = nuevoNodo;
	}
	else{
		primero();
		while ((!finLista()) && (!stop)){
			nodoActual = this->nodoActual;
			if (*(obtenerActual()) > *(xdato)){
				if (nodoAnterior) nodoAnterior->siguiente = nuevoNodo; else lista = nuevoNodo;
				nuevoNodo->siguiente = nodoActual;
				stop = true;
			}
			nodoAnterior = nodoActual;
		}
		if (!stop){
			ultimoNodo->siguiente = nuevoNodo;
			ultimoNodo = nuevoNodo;
		}
	}
}

template <class T>
/* Metodo: AgregarAlFinal
 * Agrega un elemento al final de la lista. */
void tLista<T>::agregarAlFinal(T *xdato){
	tNodoLista<T> *nuevoNodo = new tNodoLista<T>(xdato);

	if (lista == NULL)
		lista = nuevoNodo;
	else
		ultimoNodo->siguiente = nuevoNodo;

	ultimoNodo = nuevoNodo;
}

template <class T>
/* Metodo: eliminar
 * Borra un elemento en particular de la lista.*/
bool tLista<T>::eliminar(T *xdato){
	bool found = false;
	tNodoLista<T> *nodoAnterior = NULL;
	tNodoLista<T> *nodoActual = NULL;

	if (lista != NULL){
		primero();
		while ((!finLista()) && (!found)){
			nodoActual = this->nodoActual;
			//if (*(obtenerActual()) == *(xdato)){
			if ((obtenerActual()) == (xdato)){
				if (nodoAnterior) nodoAnterior->siguiente = nodoActual->siguiente; else lista = nodoActual->siguiente;
				if (ultimoNodo == nodoActual) ultimoNodo = nodoAnterior;
				delete nodoActual;
				found = true;
			}
			nodoAnterior = nodoActual;
		}
		if (!found) return false; else return true;
	}
	return false;
}

template <class T>
/* Metodo: Buscar
 * Busca un elemento dentro de la lista. */
bool tLista<T>::buscar(T *xdato){
	bool found = false;
	tNodoLista<T> *nodoActual = NULL;

	if (lista != NULL){
		primero();
		while ((!finLista()) && (!found)){
			nodoActual = this->nodoActual;
			if (*(obtenerActual()) == *(xdato)) found = true;
		}
		if (!found) return false;
		else {
			this->nodoActual = nodoActual;
			return true;
		}
	} else return false;
}

template <class T>
/* Metodo: vaciarLista
 * Elimina los nodos que componen la lista.*/
void tLista<T>::vaciarLista(){
	tNodoLista<T> *pNodo = lista;

	while (pNodo){
		tNodoLista<T> *aux = pNodo;
		pNodo = pNodo->siguiente;
		delete aux;
	}
	lista = ultimoNodo = nodoActual = NULL;
}

template <class T>
/* Metodo: operator=
 * Sobre carga de operador, de asignacion de listas.*/
tLista<T> tLista<T>::operator=(tLista<T> list){
	vaciarLista();
	list.primero();
	while (!list.finLista())
		agregarAlFinal(list.obtenerActual());

	return *this;
}

template <class T>
/* Metodo: primero
 * Setea al atributo NodoActual que apunte al primer elemento de la lista.*/
void tLista<T>::primero(){
	nodoActual = lista;
}

template <class T>
/* Metodo: finLista
 * Devuelve true o false, si llego al final de la lista. */
bool tLista<T>::finLista(){
	if (nodoActual) return false;
	return true;
}

/*
	Metodo: obtenerActual
	pre: la lista tuvo que ser creada
	pos: devuelve el dato actual y posiciona el puntero actual al siguiente.
 */
template <class T>
T  *tLista<T>::obtenerActual(){
	T *dato = nodoActual->dato;
	nodoActual = nodoActual->siguiente;
	return dato;
}

template <class T>
/* Metodo: obtenerActual
   Devuelve el puntero al dato del atributo nodoActual. */
T  *tLista<T>::obtenerUltimo(){
	T *dato = ultimoNodo->dato;
	return dato;
}

/*
	Metodo: obtenerNodo
	pre: la lista tuvo que ser creada
	pos: devuelve el dato actual.
 */
template <class T>
T  *tLista<T>::obtenerNodo(){
	T *dato = nodoActual->dato;
	return dato;
}

#endif	/* _CLIST_H */
